/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.model.codes;

import org.zhiqim.kernel.constants.HttpConstants;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * URI [scheme://host[:port]]/path[;jsessionid][?query]
 * 
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
public class URI implements HttpConstants
{
    private static final String SESSION_KEY     = ";jsessionid=";
    private static final String DOMAIN_OR_END   = "-_." + _LETTERS_DIGITS_ + ":/";//支持如2015_t.zhiqim-er.org，暂不支持中文域名
    private static final String SESSION_OR_END  = "-_." + _LETTERS_DIGITS_ + "?"; //支持-_.作连接符
    
    private boolean parsed;
    
    private String url;
    private String scheme = _HTTP_;
    private String host;
    private int port;
    private String path;
    private String sessionId;
    private String sessionIdName;
    private String query;

    /**
     * 解析URL为URI信息
     * 
     * @param url [scheme://host[:port]]/path[;jsessionid][?query]
     * @return =true 表示成功，=false 表示失败
     */
    public URI parse(String url)
    {
        this.url = url;
        
        //1.检查协议头
        boolean hasHost = true;
        if (Strings.startsWithIgnoreCase(url, _HTTP_P_))
        {
            url = url.substring(7);
            scheme = _HTTP_;
        }
        else if (Strings.startsWithIgnoreCase(url, _HTTPS_P_))
        {
            url = url.substring(8);
            scheme = _HTTPS_;
        }
        else if (Strings.startsWithIgnoreCase(url, _WS_P_))
        {
            url = url.substring(5);
            scheme = _WS_;
        }
        else if (Strings.startsWithIgnoreCase(url, _WSS_P_))
        {
            url = url.substring(6);
            scheme = _WSS_;
        }
        else
        {//相对路径/开头,未提供host
            hasHost = false;
            port = 80;
        }
        
        int i = 0;
        if (hasHost)
        {//2.读取主机信息
            StringBuilder strb = new StringBuilder();
            for (;i<url.length();i++)
            {
                char c = url.charAt(i);
                if (!Validates.isCharInString(c, DOMAIN_OR_END))
                {//域名或IP地址格式不正确
                    return this;
                }
                
                if (c == ':')
                {//有PORT情况
                    break;//host结束
                }
                
                if (c == '/')
                {//有PORT情况
                    port = isHttps()?443:80;
                    break;
                }
                
                strb.append(c);
            }
            
            if (strb.length() == 0)
            {//未读到HOST，解析失败
                return this;
            }
            
            host = strb.toString().toLowerCase();
            url = url.substring(i);
            if (url.length() == 0)
            {//仅HOST情况结尾
                port = isHttps()?443:80;
            }
        }
        
        if (port == 0 && url.length() > 0)
        {//3.读取端口信息
            if (!url.startsWith(":"))
                return this;
            
            url = url.substring(1);
            StringBuilder strb = new StringBuilder();
            for (i=0;i<url.length();i++)
            {
                char c = url.charAt(i);
                if (!Validates.isCharInString(c, "/0123456789"))
                {//端口格式不正确
                    return this;
                }
                
                if (c == '/')
                {//port结束
                    break;
                }
                
                strb.append(c);
            }
            
            if (strb.length() == 0)
            {//未读到PORT，解析失败
                return this;
            }
            
            port = Integer.parseInt(strb.toString());
            url = url.substring(i);
        }
        
        boolean hasSessionId = false;
        if (url.length() > 0)
        {//4.读取路径信息
            if (!url.startsWith("/"))
                return this;
            
            StringBuilder strb = new StringBuilder();
            for (i=0;i<url.length();i++)
            {
                char c = url.charAt(i);
                if (c == '?')
                {//path结束，后续为queryString
                    break;
                }
                
                if (c == ';')
                {//path结束，后续为jsessionId
                    hasSessionId = true;
                    break;
                }
               
                strb.append(c);
            }
            
            if (strb.length() > 0)
            {
                path = strb.toString();
                url = url.substring(i);
            }
        }
        
        if (hasSessionId && url.length() > 0)
        {//读取会话标识信息
            if (!url.startsWith(";"))
                return this;

            String sesseionKey = "";
            if (url.startsWith(SESSION_KEY))
            {
                sessionIdName = "jsessionid";
                sesseionKey = SESSION_KEY;
            }
            else
            {//读取sessionIdName
                StringBuilder strbName = new StringBuilder();
                for (i=0;i<url.length();i++)
                {
                    char c = url.charAt(i);
                    if (c == '=')
                    {
                        break;
                    }
                    strbName.append(c);
                }
                
                if (strbName.length() > 1)
                {
                    sessionIdName = strbName.substring(1).toString();
                    sesseionKey = strbName.append('=').toString();
                }
            }

            url = url.substring(sesseionKey.length());

            StringBuilder strb = new StringBuilder();
            for (i=0;i<url.length();i++)
            {
                char c = url.charAt(i);
                if (!Validates.isCharInString(c, SESSION_OR_END))
                {//jsessionid格式不正确
                    return this;
                }
                
                if (c == '?')
                {//jsessionid结束，后续为queryString
                    break;
                }
                
                strb.append(c);
            }
            
            if (strb.length() > 0)
            {
                sessionId = strb.toString();
                url = url.substring(i);
            }
        }
        
        if (url.length() > 0)
        {
            if (!url.startsWith("?"))
                return this;
            
            query = url.substring(1);
        }
        
        //解析成功
        parsed = true;
        return this;
    }
    
    @Override
    public String toString()
    {
        return url;
    }
    
    public boolean isParsed()
    {
        return parsed;
    }
    
    public String getScheme()
    {
        return scheme;
    }
    
    public boolean isHttps()
    {
        return _HTTPS_.equals(scheme) || _WSS_.equals(scheme);
    }
    
    public String getHost()
    {
        return host;
    }
    
    public int getPort()
    {
        return port;
    }
    
    public String getPath()
    {
        return path;
    }
    
    public String getSessionId()
    {
        if (Strings.equals("jsessionid", sessionIdName))
            return sessionId;
        return null;
    }
    
    public String getSessionId(String name)
    {
        if (Strings.equals(name, sessionIdName))
            return sessionId;
        return null;
    }
    
    public String getQuery()
    {
        return query;
    }

    /** 从path中解释虚拟目录 */
    public String getVirtualDirectory()
    {
        int ind = path.indexOf('/', 1);
        if (ind != -1)
            return path.substring(0, ind);//虚拟目录 如 /abc/test.html 
        else
            return "/";//根目录, 如 /abc.html 
    }
    
    /** 获取服务端根地址，不含最后的斜杠 */
    public String getRootUrl()
    {
        if (port == 80)
            return scheme + "://" + host;
        else
            return scheme + "://" + host + ":"+ port;
    }
    
    /** 获取服务端根地址，含最后的斜杠 */
    public String getRootUrlSlash()
    {
        if (port == 80)
            return scheme + "://" + host + "/";
        else
            return scheme + "://" + host + ":"+ port +"/";
    }
    
    /** 获取PATH+QUERY */
    public String getPathQuery()
    {
        Asserts.as(parsed?null:"URL未解析或解析失败");
        if (path == null)
            return "/";
        
        if (Validates.isEmptyBlank(query))
            return path;
        
        return path + "?" + query;
    }
}
